module snake#(
	// the maximum length of the snake
	parameter SNAKE_MAX_LEN = 32,
	parameter LAUNCHING=2'b00,
	parameter PLAYING=2'b01,
	parameter DIE_FLASHING=2'b10,
	parameter INITIALIZING=2'b11,

	parameter BLOCK_X_MAX = 7'd79,
	parameter BLOCK_Y_MAX = 7'd59,

	parameter UP=2'b00,
	parameter RIGHT=2'b01,
	parameter DOWN=2'b10,
	parameter LEFT=2'b11

)(
	input clk, 									// 时钟信号输入

	input game_pause,					 		// 游戏暂停信号输入
	input [1:0] game_status, 					// 游戏状态输入
	input [1:0] game_level, 					// 游戏等级输入

	input [1:0] current_direction, 				// 当前蛇移动方向输入

	input [6:0] apple_x, 						// 苹果x坐标输入
	input [6:0] apple_y, 						// 苹果y坐标输入
	
    output [SNAKE_MAX_LEN*7-1:0] snake_x_bus, 	// 蛇所有身体部分x坐标输出总线
	output [SNAKE_MAX_LEN*7-1:0] snake_y_bus, 	// 蛇所有身体部分y坐标输出总线

	output reg [31:0] snake_piece_is_display, 	// 蛇身体各部分是否显示标志输出
	output reg get_apple, 						// 是否吃到苹果标志输出
	output reg hit_wall, 						// 是否撞墙标志输出
	output reg hit_itself 						// 是否撞到自己标志输出
);

// ************ parameter define ***********
reg [25:0] count; // 计数器1
reg [25:0] count_two; // 计数器2
reg [31:0] snake_piece_is_display_origin; // 存储体长的旧值，用于死亡闪烁

initial
begin
	count <= 0; // 初始化计数器1为0
	count_two <= 0; // 初始化计数器2为0
end

localparam CLK_CNT_MAX  = 27'd50_000_000; // 时钟计数最大值
    
// clk counter
reg [27:0] clk_cnt; // 时钟计数器
    
// snake index
reg [6:0] snake_x [SNAKE_MAX_LEN-1:0]; // 蛇身体各部分x坐标数组
reg [6:0] snake_y [SNAKE_MAX_LEN-1:0]; // 蛇身体各部分y坐标数组

reg move_forward; // 蛇是否向前移动标志

// block is wall
wire is_wall; // 是否撞墙信号
// block is apple
wire is_apple; // 是否吃到苹果信号
// block is snake body
wire is_snake_body; // 是否撞到自己信号

assign is_wall = game_level == 'd0 ? 
	(snake_x[0]==0 || snake_x[0]==BLOCK_X_MAX || snake_y[0]==0 || snake_y[0]==BLOCK_Y_MAX) : 
	(snake_x[0]==0 || snake_x[0]==BLOCK_X_MAX || snake_y[0]==0 || snake_y[0]==BLOCK_Y_MAX 
	|| (snake_y[0]==BLOCK_Y_MAX/3 && snake_x[0]>=BLOCK_X_MAX/4 && snake_x[0]<=BLOCK_X_MAX/4*3)
	|| (snake_x[0]==BLOCK_X_MAX/4 && snake_y[0]>=BLOCK_Y_MAX/3 && snake_y[0]<=BLOCK_Y_MAX/3*2)
	|| (snake_x[0]==BLOCK_X_MAX/4*3 && snake_y[0]>=BLOCK_Y_MAX/3 && snake_y[0]<=BLOCK_Y_MAX/3*2)
	);
assign is_apple = snake_x[0]==apple_x && snake_y[0]==apple_y;
assign is_snake_body = (
	(snake_x[0]==snake_x[1] && snake_y[0]==snake_y[1] && snake_piece_is_display[1]) ||
	(snake_x[0]==snake_x[2] && snake_y[0]==snake_y[2] && snake_piece_is_display[2]) ||
	(snake_x[0]==snake_x[3] && snake_y[0]==snake_y[3] && snake_piece_is_display[3]) ||
	(snake_x[0]==snake_x[4] && snake_y[0]==snake_y[4] && snake_piece_is_display[4]) ||
	(snake_x[0]==snake_x[5] && snake_y[0]==snake_y[5] && snake_piece_is_display[5]) ||
	(snake_x[0]==snake_x[6] && snake_y[0]==snake_y[6] && snake_piece_is_display[6]) ||
	(snake_x[0]==snake_x[7] && snake_y[0]==snake_y[7] && snake_piece_is_display[7]) ||
	(snake_x[0]==snake_x[8] && snake_y[0]==snake_y[8] && snake_piece_is_display[8]) ||
	(snake_x[0]==snake_x[9] && snake_y[0]==snake_y[9] && snake_piece_is_display[9]) ||
	(snake_x[0]==snake_x[10] && snake_y[0]==snake_y[10] && snake_piece_is_display[10]) ||
	(snake_x[0]==snake_x[11] && snake_y[0]==snake_y[11] && snake_piece_is_display[11]) ||
	(snake_x[0]==snake_x[12] && snake_y[0]==snake_y[12] && snake_piece_is_display[12]) ||
	(snake_x[0]==snake_x[13] && snake_y[0]==snake_y[13] && snake_piece_is_display[13]) ||
	(snake_x[0]==snake_x[14] && snake_y[0]==snake_y[14] && snake_piece_is_display[14]) ||
	(snake_x[0]==snake_x[15] && snake_y[0]==snake_y[15] && snake_piece_is_display[15]) ||
	(snake_x[0]==snake_x[16] && snake_y[0]==snake_y[16] && snake_piece_is_display[16]) ||
	(snake_x[0]==snake_x[17] && snake_y[0]==snake_y[17] && snake_piece_is_display[17]) ||
	(snake_x[0]==snake_x[18] && snake_y[0]==snake_y[18] && snake_piece_is_display[18]) ||
	(snake_x[0]==snake_x[19] && snake_y[0]==snake_y[19] && snake_piece_is_display[19]) ||
	(snake_x[0]==snake_x[20] && snake_y[0]==snake_y[20] && snake_piece_is_display[20]) ||
	(snake_x[0]==snake_x[21] && snake_y[0]==snake_y[21] && snake_piece_is_display[21]) ||
	(snake_x[0]==snake_x[22] && snake_y[0]==snake_y[22] && snake_piece_is_display[22]) ||
	(snake_x[0]==snake_x[23] && snake_y[0]==snake_y[23] && snake_piece_is_display[23]) ||
	(snake_x[0]==snake_x[24] && snake_y[0]==snake_y[24] && snake_piece_is_display[24]) ||
	(snake_x[0]==snake_x[25] && snake_y[0]==snake_y[25] && snake_piece_is_display[25]) ||
	(snake_x[0]==snake_x[26] && snake_y[0]==snake_y[26] && snake_piece_is_display[26]) ||
	(snake_x[0]==snake_x[27] && snake_y[0]==snake_y[27] && snake_piece_is_display[27]) ||
	(snake_x[0]==snake_x[28] && snake_y[0]==snake_y[28] && snake_piece_is_display[28]) ||
	(snake_x[0]==snake_x[29] && snake_y[0]==snake_y[29] && snake_piece_is_display[29]) ||
	(snake_x[0]==snake_x[30] && snake_y[0]==snake_y[30] && snake_piece_is_display[30]) ||
	(snake_x[0]==snake_x[31] && snake_y[0]==snake_y[31] && snake_piece_is_display[31])
);

genvar i;
generate
	for(i=0;i<SNAKE_MAX_LEN;i=i+1)begin:loop
		assign snake_x_bus[7*i+:7]=snake_x[i]; // 将蛇身体各部分x坐标分配到总线
    	assign snake_y_bus[7*i+:7]=snake_y[i]; // 将蛇身体各部分y坐标分配到总线
	end
endgenerate

always @(posedge clk) begin
	case(game_status)
		LAUNCHING:begin
			snake_piece_is_display<=32'b00000000_00000000_00000000_00011111; // 初始化蛇身体显示标志
			snake_x[0]<=14; // 初始化蛇头x坐标
			snake_y[0]<=20; // 初始化蛇头y坐标
			snake_x[1]<=13; // 
			snake_y[1]<=20; // 
			snake_x[2]<=12; // 
			snake_y[2]<=20; // 
			snake_x[3]<=11; // 
			snake_y[3]<=20; // 
			snake_x[4]<=10; // 
			snake_y[4]<=20; // 
			hit_wall<=0; // 初始化撞墙标志
			hit_itself<=0; // 初始化撞到自己标志
			get_apple<=0; // 初始化吃到苹果标志
			clk_cnt<='d0; // 初始化时钟计数器
			move_forward<='b0; // 初始化蛇是否向前移动标志
		end
		INITIALIZING:begin
			snake_piece_is_display<=32'b00000000_00000000_00000000_00011111; // 初始化蛇身体显示标志
			snake_x[0]<=14; // 初始化蛇头x坐标
			snake_y[0]<=20; // 初始化蛇头y坐标
			snake_x[1]<=13; // 初始化蛇身体第1节x坐标
			snake_y[1]<=20; // 
			snake_x[2]<=12; // 
			snake_y[2]<=20; // 
			snake_x[3]<=11; // 
			snake_y[3]<=20; // 
			snake_x[4]<=10; // 
			snake_y[4]<=20; // 
			hit_wall<=0; // 初始化撞墙标志
			hit_itself<=0; // 初始化撞到自己标志
			get_apple<=0; // 初始化吃到苹果标志
			clk_cnt<='d0; // 初始化时钟计数器
			move_forward<='b0; // 初始化蛇是否向前移动标志
		end
		DIE_FLASHING:begin	 
			if (count_two>=40000000) count_two<=0; // 计数器2达到最大值后清零
			else count_two<=count_two+1; // 计数器2递增			   	
    		// 闪烁
    		if (count_two==20000000) 
				snake_piece_is_display<=0; // 蛇身体闪烁消失
    		else if (count_two==0) 
				snake_piece_is_display<=snake_piece_is_display_origin; // 蛇身体闪烁恢复
				hit_wall<=0; // 初始化撞墙标志
				hit_itself<=0; // 初始化撞到自己标志
				get_apple<=0; // 初始化吃到苹果标志
				clk_cnt<='d0; // 初始化时钟计数器
				move_forward<='b0; // 初始化蛇是否向前移动标志
		end
		PLAYING:begin
			snake_piece_is_display_origin<=snake_piece_is_display; // 存储体长的旧值，用于死亡闪烁

			hit_wall<=0; // 初始化撞墙标志
			hit_itself<=0; // 初始化撞到自己标志
			get_apple<=0; // 初始化吃到苹果标志
			
			move_forward<='b0; // 初始化蛇是否向前移动标志
			if(is_wall)begin
				hit_wall<=1; // 设置撞墙标志
			end
			if(is_snake_body)begin
				hit_itself<=1; // 设置撞到自己标志
			end
			if(is_apple)begin
				get_apple<=1; // 设置吃到苹果标志
			end
			if(get_apple)begin
				snake_piece_is_display<={snake_piece_is_display[SNAKE_MAX_LEN-2:0],1'b1}; // 增加蛇身体长度
			end
			if(~game_pause)begin
				clk_cnt<=clk_cnt+1'b1; // 时钟计数器递增
				if(game_level == 'd0 || game_level == 'd1)begin
					if(clk_cnt == CLK_CNT_MAX)begin
						clk_cnt<='d0; // 时钟计数器清零
						move_forward<='b1; // 设置蛇向前移动标志
					end
				end
				else if(game_level == 'd2)begin
					if(clk_cnt == CLK_CNT_MAX/2)begin
						clk_cnt<='d0; // 时钟计数器清零
						move_forward<='b1; // 设置蛇向前移动标志
					end
				end
			end
			if(move_forward && ~game_pause)begin
				case (current_direction)
				UP:
					begin
					snake_x[0]<=snake_x[0]; // 蛇头x坐标不变
					snake_y[0]<=snake_y[0]-1; // 蛇头y坐标减1
					end
				RIGHT:
					begin
					snake_x[0]<=snake_x[0]+1; // 蛇头x坐标加1
					snake_y[0]<=snake_y[0]; // 蛇头y坐标不变
					end
				DOWN:
					begin
					snake_x[0]<=snake_x[0]; // 蛇头x坐标不变
					snake_y[0]<=snake_y[0]+1; // 蛇头y坐标加1
					end
				LEFT:
					begin
					snake_x[0]<=snake_x[0]-1; // 蛇头x坐标减1
					snake_y[0]<=snake_y[0]; // 蛇头y坐标不变
					end
				default:
					begin
					snake_x[0]<=snake_x[0]; // 蛇头x坐标不变
					snake_y[0]<=snake_y[0]; // 蛇头y坐标不变
					end
				endcase
				// 从最开始，依次将每个蛇身部分的位置更新为其前一个部分的位置
				snake_x[1]<=snake_x[0]; // 更新蛇身体第1节x坐标
				snake_y[1]<=snake_y[0]; // 更新蛇身体第1节y坐标
				snake_x[2]<=snake_x[1]; // 更新蛇身体第2节x坐标
				snake_y[2]<=snake_y[1]; // 更新蛇身体第2节y坐标
				snake_x[3]<=snake_x[2]; // 更新蛇身体第3节x坐标
				snake_y[3]<=snake_y[2]; // 更新蛇身体第3节y坐标
				snake_x[4]<=snake_x[3]; // 更新蛇身体第4节x坐标
				snake_y[4]<=snake_y[3]; // 更新蛇身体第4节y坐标
				snake_x[5]<=snake_x[4]; // 更新蛇身体第5节x坐标
				snake_y[5]<=snake_y[4]; // 更新蛇身体第5节y坐标
				snake_x[6]<=snake_x[5]; // 更新蛇身体第6节x坐标
				snake_y[6]<=snake_y[5]; // 更新蛇身体第6节y坐标
				snake_x[7]<=snake_x[6]; // 更新蛇身体第7节x坐标
				snake_y[7]<=snake_y[6]; // 更新蛇身体第7节y坐标
				snake_x[8]<=snake_x[7]; // 更新蛇身体第8节x坐标
				snake_y[8]<=snake_y[7]; // 更新蛇身体第8节y坐标
				snake_x[9]<=snake_x[8]; // 更新蛇身体第9节x坐标
				snake_y[9]<=snake_y[8]; // 更新蛇身体第9节y坐标
        		snake_y[10] <= snake_y[9];  
        		snake_x[11] <= snake_x[10]; 
        		snake_y[11] <= snake_y[10]; 
        		snake_x[12] <= snake_x[11]; 
        		snake_y[12] <= snake_y[11]; 
        		snake_x[13] <= snake_x[12]; 
        		snake_y[13] <= snake_y[12]; 
        		snake_x[14] <= snake_x[13]; 
        		snake_y[14] <= snake_y[13]; 
        		snake_x[15] <= snake_x[14]; 
        		snake_y[15] <= snake_y[14]; 
        		snake_x[16] <= snake_x[15]; 
        		snake_y[16] <= snake_y[15]; 
        		snake_x[17] <= snake_x[16]; 
        		snake_y[17] <= snake_y[16]; 
        		snake_x[18] <= snake_x[17]; 
        		snake_y[18] <= snake_y[17]; 
        		snake_x[19] <= snake_x[18]; 
        		snake_y[19] <= snake_y[18]; 
        		snake_x[20] <= snake_x[19]; 
        		snake_y[20] <= snake_y[19]; 
        		snake_x[21] <= snake_x[20]; 
        		snake_y[21] <= snake_y[20]; 
        		snake_x[22] <= snake_x[21]; 
        		snake_y[22] <= snake_y[21]; 
        		snake_x[23] <= snake_x[22]; 
        		snake_y[23] <= snake_y[22]; 
        		snake_x[24] <= snake_x[23]; 
        		snake_y[24] <= snake_y[23]; 
        		snake_x[25] <= snake_x[24]; 
        		snake_y[25] <= snake_y[24]; 
        		snake_x[26] <= snake_x[25]; 
        		snake_y[26] <= snake_y[25]; 
        		snake_x[27] <= snake_x[26]; 
        		snake_y[27] <= snake_y[26]; 
        		snake_x[28] <= snake_x[27]; 
        		snake_y[28] <= snake_y[27]; 
        		snake_x[29] <= snake_x[28]; 
        		snake_y[29] <= snake_y[28]; 
        		snake_x[30] <= snake_x[29]; 
        		snake_y[30] <= snake_y[29]; 
        		snake_x[31] <= snake_x[30]; 
        		snake_y[31] <= snake_y[30]; 
    	end
	end
    default: begin
        // 初始化蛇的显示状态，只有前5节蛇身显示
        snake_piece_is_display <= 32'b00000000_00000000_00000000_00011111;
        // 初始化蛇头位置
        snake_x[0] <= 14; // 蛇头x坐标初始化为14
        snake_y[0] <= 20; // 蛇头y坐标初始化为20
        // 初始化蛇身其他部分的位置
        snake_x[1] <= 13; 
        snake_y[1] <= 20; 
        snake_x[2] <= 12; 
        snake_y[2] <= 20; 
        snake_x[3] <= 11; 
        snake_y[3] <= 20; 
        snake_x[4] <= 10; 
        snake_y[4] <= 20; 
        // 初始化碰撞标志
        hit_wall <= 0;   // 没有撞墙
        hit_itself <= 0; // 没有撞到自己
        get_apple <= 0;  // 没吃到苹果
        // 初始化计数器和移动标志
        clk_cnt <= 'd0;      // 时钟计数器清零
        move_forward <= 'b0; // 移动标志清零
    end
	endcase
end
endmodule